package com.zhou.cloud.service.impl;

import com.zhou.cloud.config.UuidProperties;
import com.zhou.cloud.service.IGeneratorUuidService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @descrition: 雪花算法 实现uuid生成
 * @author: zhouyongjun
 * @date: 2022/10/31
 **/
@Service("snowflakeUuidService")
@Slf4j
public class SnowflakeUuidServcieImpl implements IGeneratorUuidService {
    //首位是0,后63位是1
    /*long max = -1 >>> 1;*/
    long maxBit = 63;
    long max = Long.MAX_VALUE;
    long dateTimeBits = 41;
    long workBit = 5;
    long datacenterBit = 5;
    long sequenceBits  = 63 - dateTimeBits - datacenterBit - workBit;
    //最大数量
    final long sequenceMask = max >> maxBit - sequenceBits;
    //当前数量
    long sequenc = 0;
    long lastDateTimeMillis;
    @Autowired
    UuidProperties uuidProperties;
    public void fristInTheSameSecond() {
        sequenc = 0;
    }

    @Override
    public synchronized Long generatorUuid() {
        long timeMillis = dateTimeMillis();
        if (lastDateTimeMillis == 0 || lastDateTimeMillis == timeMillis) {
            //同一个毫秒内生成
            if (sequenc >= sequenceMask) {
                //等待下一毫秒才行生成id
                while(timeMillis == dateTimeMillis()){ }
                timeMillis = dateTimeMillis();
                fristInTheSameSecond();
            }
        }if (timeMillis < lastDateTimeMillis) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastDateTimeMillis - timeMillis));
        }
        else {
            fristInTheSameSecond();
        }
        //+1操作
        sequenc ++;
        //设置最后时间
        lastDateTimeMillis = timeMillis;
        //生成uuid
        /*
            timeMillis 偏移 63-41 位
            centerId  偏移 63-41-5 位
            ZoneId  偏移 63-41-5-5 位
         */
        long uuid = timeMillis << maxBit-dateTimeBits
                | uuidProperties.getCenterId() << maxBit-dateTimeBits-datacenterBit
                | uuidProperties.getZoneId() << maxBit-dateTimeBits-datacenterBit-workBit
                |sequenc;
        log.warn("result uuid={},timeMillis={},center={},zoneId={}, sequenc={},lastDateTimeMillis={}",uuid,timeMillis,uuidProperties.getCenterId(),uuidProperties.getZoneId(),sequenc,lastDateTimeMillis);
        log.warn("\tui bit:\t"+Long.toBinaryString(uuid)+"\t size="+String.valueOf(Long.toBinaryString(uuid)).toCharArray().length);
        log.warn("\ttm bit:\t"+Long.toBinaryString(timeMillis)+"\t size="+String.valueOf(Long.toBinaryString(timeMillis)).toCharArray().length
                +" <<"+Long.toBinaryString(timeMillis << maxBit-dateTimeBits));
        log.warn("\tci bit:\t"+Long.toBinaryString(uuidProperties.getCenterId())+"\t size="+String.valueOf(Long.toBinaryString(uuidProperties.getCenterId())).toCharArray().length
                +" <<"+Long.toBinaryString(uuidProperties.getCenterId() << maxBit-dateTimeBits-datacenterBit));
        log.warn("\tzi bit:\t"+Long.toBinaryString(uuidProperties.getZoneId())+"\t size="+String.valueOf(Long.toBinaryString(uuidProperties.getZoneId())).toCharArray().length
                +" <<"+Long.toBinaryString(uuidProperties.getZoneId() << maxBit-dateTimeBits-datacenterBit-workBit));
        log.warn("\tse bit:\t"+Long.toBinaryString(sequenc)+"\t size="+String.valueOf(Long.toBinaryString(sequenc)).toCharArray().length);
        return uuid;
    }

    public long dateTimeMillis(){
        return System.currentTimeMillis();
    }

}
